/*
 * Copyright (c) 2008, Spagettikod | Stockholm
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of the Spagettikod | Stockholm nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.spagettikod.t437;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import apple.dts.samplecode.osxadapter.OSXAdapter;

import com.spagettikod.SystemUtil;
import com.spagettikod.t437.component.SimpleDBJPanel;
import com.spagettikod.t437.component.domain.DomainJList;
import com.spagettikod.t437.dialog.About;
import com.spagettikod.t437.dialog.Preferences;
import com.xerox.amazonws.sdb.SDBException;

public class T437 implements ActionListener
{
	private static final Log log = LogFactory.getLog(T437.class);

	private static final String APP_NAME = "T-437";

	public static final String VERSION = "0.7.2";

	private static JFrame frame;

	private static Configurator config;

	private static SimpleDBJPanel simpleDBPanel;

	private DomainJList domainList;

	private JSplitPane splitPane;

	public T437()
	{
		config = new Configurator();

		frame = new JFrame(APP_NAME);
		frame.setBounds(config.getMainWindowBounds());
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// Add shutdown hook which saves user settings.
		Runtime.getRuntime().addShutdownHook(new Thread()
		{

			@Override
			public void run()
			{
				shutdown();
			}

		});

		setLookAndFeel();

		if (!config.isAccessConfigured())
		{
			preferences(Preferences.Tabs.AWS, true);
		}

		createAndShowGUI();
	}

	public static Configurator getConfigurator()
	{
		return config;
	}

	private void setLookAndFeel()
	{
		JFrame.setDefaultLookAndFeelDecorated(true);
		try
		{
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (ClassNotFoundException e)
		{
			log.error("Could not set look and feel.", e);
		} catch (InstantiationException e)
		{
			log.error("Could not set look and feel.", e);
		} catch (IllegalAccessException e)
		{
			log.error("Could not set look and feel.", e);
		} catch (UnsupportedLookAndFeelException e)
		{
			log.error("Could not set look and feel.", e);
		}
	}

	private DomainJList initDomainList()
	{
		try
		{
			return new DomainJList();
		} catch (SDBException e)
		{
			displayErrorMessage("Initialization error", e.getMessage());
		}
		return null;
	}

	/**
	 * 
	 */
	private void createAndShowGUI()
	{

		simpleDBPanel = new SimpleDBJPanel();

		// Keep showing the preferences dialog until list of domains can be
		// populated without an error. We kind of take for granted that the
		// error that occurs has something to do with the AWS keys.
		while ((domainList = initDomainList()) == null)
		{
			preferences(Preferences.Tabs.AWS, true);

		}
		domainList.addDomainChangeListener(simpleDBPanel);
		domainList.addDomainDoubleClickListener(simpleDBPanel);

		JScrollPane domainScrollPane = new JScrollPane(domainList);

		splitPane = new JSplitPane();
		splitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
		splitPane.setLeftComponent(domainScrollPane);
		splitPane.setRightComponent(simpleDBPanel);
		splitPane.setDividerLocation(config.getSplitDividerLocation());

		frame.getContentPane().add(splitPane);

		JMenuBar menubar = new JMenuBar();
		JMenuItem menuItem;

		if (SystemUtil.isMacOSX())
		{
			try
			{
				OSXAdapter.setQuitHandler(this, getClass().getDeclaredMethod(
						"quit", (Class[]) null));
				OSXAdapter.setAboutHandler(this, getClass().getDeclaredMethod(
						"about", (Class[]) null));
				OSXAdapter.setPreferencesHandler(this, getClass()
						.getDeclaredMethod("preferences", (Class[]) null));
			} catch (Exception e)
			{
				log.error("Error while loading the OSXAdapter", e);
			}
		} else
		{
			// Create menus shown when not running Mac OS X
			JMenu fileMenu = new JMenu("File");
			fileMenu.setMnemonic(KeyEvent.VK_F);

			menuItem = new JMenuItem("Preferences...");
			menuItem.setActionCommand("preferences");
			menuItem.addActionListener(this);

			fileMenu.add(menuItem);

			fileMenu.addSeparator();

			menuItem = new JMenuItem("Exit");
			menuItem.setActionCommand("quit");
			menuItem.addActionListener(this);
			menuItem.setMnemonic(KeyEvent.VK_X);
			fileMenu.add(menuItem);

			menubar.add(fileMenu);
		}

		// TODO Extract this to it's own class which include methods for easy
		// enabling and disabling of menu items.
		JMenu sdbMenu = new JMenu("SimpleDB");
		sdbMenu.setMnemonic(KeyEvent.VK_S);

		menuItem = new JMenuItem("Execute Query");
		menuItem.setActionCommand("sdbFilter");
		menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
				InputEvent.CTRL_MASK));
		menuItem.addActionListener(this);
		menuItem.setEnabled(true);
		sdbMenu.add(menuItem);

		menuItem = new JMenuItem("Fit Columns To Content");
		menuItem.setActionCommand("sdbFitContent");
		menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F,
				InputEvent.CTRL_MASK));
		menuItem.addActionListener(this);
		menuItem.setEnabled(true);
		sdbMenu.add(menuItem);

		menuItem = new JMenuItem("Refresh Domain List");
		menuItem.setActionCommand("sdbRefreshDomains");
		// TODO Find good shortcut, or maybe skip shortcut?
		// menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F,
		// InputEvent.CTRL_MASK));
		menuItem.addActionListener(this);
		menuItem.setEnabled(true);
		sdbMenu.add(menuItem);

		sdbMenu.setEnabled(true);
		menubar.add(sdbMenu);

		if (!SystemUtil.isMacOSX())
		{
			JMenu helpMenu = new JMenu("Help");
			helpMenu.setMnemonic(KeyEvent.VK_H);

			menuItem = new JMenuItem("About...");
			menuItem.setActionCommand("about");
			menuItem.addActionListener(this);
			menuItem.setMnemonic(KeyEvent.VK_A);
			helpMenu.add(menuItem);

			menubar.add(helpMenu);
		}

		frame.setJMenuBar(menubar);

		// Display the window.
		frame.setPreferredSize(new Dimension(640, 480));
		if (config.getMainWindowBounds().width == 0)
		{
			frame.pack();
		}
		frame.setVisible(true);
	}

	/**
	 * Called when exiting the application on Mac OS X.
	 */
	public void quit()
	{
		shutdown();
		System.exit(0);
	}

	/**
	 * Show about box.
	 */
	public void about()
	{
		new About(frame);
	}

	public void preferences(Preferences.Tabs initialTab, boolean exitOnCancel)
	{
		new Preferences(frame, config, initialTab, exitOnCancel);
		config.resetSimpleDB();
	}

	public void preferences()
	{
		preferences(Preferences.Tabs.GENERAL, false);
	}

	private void shutdown()
	{
		log.debug("Shutting down.");
		config.setMainWindowBounds(frame.getBounds());
		config.setSplitDividerLocation(splitPane.getDividerLocation());
		config.save();
	}

	public static String getApplicationTitle()
	{
		return APP_NAME;
	}

	public static String getApplicationVersion()
	{
		return VERSION;
	}

	public void actionPerformed(ActionEvent event)
	{
		log.debug(event.getActionCommand());
		if (event.getActionCommand().equals("preferences"))
		{
			preferences(Preferences.Tabs.GENERAL, false);
		} else if (event.getActionCommand().equals("quit"))
		{
			quit();
		} else if (event.getActionCommand().equals("about"))
		{
			about();
		} else if (event.getActionCommand().equals("sdbFilter"))
		{
			simpleDBPanel.executeQuery();
		} else if (event.getActionCommand().equals("sdbFitContent"))
		{
			frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
			simpleDBPanel.fitColumnWidthsToContent();
			frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		} else if (event.getActionCommand().equals("sdbRefreshDomains"))
		{
			try
			{
				domainList.refresh();
			} catch (SDBException e)
			{
				displayErrorMessage("Domain refresh failed", e.getMessage());
			}
		}
	}

	public JMenu getSimpleDBMenu()
	{
		if (SystemUtil.isMacOSX())
		{
			return frame.getJMenuBar().getMenu(0);
		} else
		{
			return frame.getJMenuBar().getMenu(1);
		}
	}

	public static JFrame getApplicationFrame()
	{
		return frame;
	}

	public static void displayErrorMessage(String title, String message)
	{
		frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		JOptionPane.showMessageDialog(frame, message, title,
				JOptionPane.ERROR_MESSAGE);
		T437.setBusyCursor(false);
	}

	public static void displayErrorMessage(Throwable exception)
	{
		JOptionPane.showMessageDialog(frame, exception.getLocalizedMessage(),
				"An error occured", JOptionPane.ERROR_MESSAGE);
		T437.setBusyCursor(false);
	}

	public static void setBusyCursor(boolean busy)
	{
		frame.getGlassPane().setVisible(busy);
		if (busy)
		{
			frame.getGlassPane().setCursor(
					Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		} else
		{
			frame.getGlassPane().setCursor(Cursor.getDefaultCursor());
		}
	}

	public static void main(String[] args)
	{
		if (SystemUtil.isMacOSX())
		{
			System.setProperty("apple.laf.useScreenMenuBar", "true");
			System.setProperty("com.apple.mrj.application.growbox.intrudes",
					"false");
			System
					.setProperty(
							"com.apple.mrj.application.apple.menu.about.name",
							APP_NAME);
		}
		javax.swing.SwingUtilities.invokeLater(new Runnable()
		{
			public void run()
			{
				new T437();
			}
		});
	}
}
